import 'dart:async';
import 'dart:io';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:mangayomi/main.dart';
import 'package:mangayomi/models/chapter.dart';
import 'package:mangayomi/models/settings.dart';
import 'package:mangayomi/modules/anime/widgets/desktop.dart';
import 'package:mangayomi/modules/manga/reader/mixins/reader_gestures.dart';
import 'package:mangayomi/modules/manga/reader/providers/crop_borders_provider.dart';
import 'package:mangayomi/modules/manga/reader/services/page_navigation_service.dart';
import 'package:mangayomi/modules/manga/reader/mixins/reader_memory_management.dart';
import 'package:mangayomi/modules/manga/reader/widgets/double_page_view.dart';
import 'package:mangayomi/modules/manga/reader/widgets/reader_app_bar.dart';
import 'package:mangayomi/modules/manga/reader/widgets/reader_bottom_bar.dart';
import 'package:mangayomi/modules/manga/reader/widgets/reader_gesture_handler.dart';
import 'package:mangayomi/modules/manga/reader/widgets/reader_settings_modal.dart';
import 'package:mangayomi/modules/manga/reader/widgets/auto_scroll_button.dart';
import 'package:mangayomi/modules/manga/reader/widgets/page_indicator.dart';
import 'package:mangayomi/modules/manga/reader/widgets/image_actions_dialog.dart';
import 'package:mangayomi/modules/more/settings/reader/providers/reader_state_provider.dart';
import 'package:mangayomi/providers/l10n_providers.dart';
import 'package:mangayomi/utils/riverpod.dart';
import 'package:mangayomi/modules/manga/reader/providers/push_router.dart';
import 'package:mangayomi/services/get_chapter_pages.dart';
import 'package:mangayomi/utils/extensions/build_context_extensions.dart';
import 'package:mangayomi/modules/manga/reader/image_view_paged.dart';
import 'package:mangayomi/modules/manga/reader/providers/reader_controller_provider.dart';
import 'package:mangayomi/modules/manga/reader/widgets/circular_progress_indicator_animate_rotate.dart';
import 'package:mangayomi/modules/manga/reader/widgets/transition_view_paged.dart';
import 'package:mangayomi/modules/more/settings/reader/reader_screen.dart';
import 'package:mangayomi/modules/manga/reader/providers/manga_reader_provider.dart';
import 'package:mangayomi/modules/manga/reader/image_view_webtoon.dart';
import 'package:mangayomi/modules/widgets/progress_center.dart';
import 'package:photo_view/photo_view.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'package:window_manager/window_manager.dart';

typedef DoubleClickAnimationListener = void Function();

class MangaReaderView extends ConsumerStatefulWidget {
  final int chapterId;
  const MangaReaderView({super.key, required this.chapterId});

  @override
  ConsumerState<MangaReaderView> createState() => _MangaReaderViewState();
}

class _MangaReaderViewState extends ConsumerState<MangaReaderView> {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      ref.invalidate(mangaReaderProvider(widget.chapterId));
    });
  }

  @override
  Widget build(BuildContext context) {
    final chapterData = ref.watch(mangaReaderProvider(widget.chapterId));

    return chapterData.when(
      loading: () => scaffoldWith(context, const ProgressCenter()),
      error: (error, _) =>
          scaffoldWith(context, Center(child: Text(error.toString()))),
      data: (data) {
        final chapter = data.chapter;
        final model = data.pages;

        if (model.pageUrls.isEmpty &&
            !(chapter.manga.value?.isLocalArchive ?? false)) {
          return scaffoldWith(
            context,
            const Center(child: Text('Error: no pages available')),
            restoreUi: true,
          );
        }

        return MangaChapterPageGallery(
          chapter: chapter,
          chapterUrlModel: model,
        );
      },
    );
  }

  Widget scaffoldWith(
    BuildContext context,
    Widget body, {
    bool restoreUi = false,
  }) {
    return Scaffold(
      backgroundColor: Theme.of(context).scaffoldBackgroundColor,
      appBar: AppBar(
        title: const Text(''),
        leading: BackButton(
          onPressed: () {
            if (restoreUi) {
              SystemChrome.setEnabledSystemUIMode(
                SystemUiMode.manual,
                overlays: SystemUiOverlay.values,
              );
            }
            Navigator.of(context).pop();
          },
        ),
      ),
      body: body,
    );
  }
}

class MangaChapterPageGallery extends ConsumerStatefulWidget {
  const MangaChapterPageGallery({
    super.key,
    required this.chapter,
    required this.chapterUrlModel,
  });
  final GetChapterPagesModel chapterUrlModel;

  final Chapter chapter;

  @override
  ConsumerState createState() {
    return _MangaChapterPageGalleryState();
  }
}

class _MangaChapterPageGalleryState
    extends ConsumerState<MangaChapterPageGallery>
    with
        TickerProviderStateMixin,
        WidgetsBindingObserver,
        ReaderMemoryManagement,
        PageNavigationMixin {
  late AnimationController _scaleAnimationController;
  late Animation<double> _animation;

  late ReaderController _readerController = ref.read(
    readerControllerProvider(chapter: chapter).notifier,
  );

  bool isDesktop = Platform.isMacOS || Platform.isLinux || Platform.isWindows;

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    _readerController.setMangaHistoryUpdate();
    _rebuildDetail.close();
    _doubleClickAnimationController.dispose();
    _scaleAnimationController.dispose();
    _failedToLoadImage.dispose();
    _autoScroll.value = false;
    _autoScroll.dispose();
    _autoScrollPage.dispose();
    _itemPositionsListener.itemPositions.removeListener(_readProgressListener);
    _photoViewController.dispose();
    _photoViewScaleStateController.dispose();
    _extendedController.dispose();
    clearGestureDetailsCache();
    if (isDesktop) {
      setFullScreen(value: false);
    } else {
      SystemChrome.setEnabledSystemUIMode(
        SystemUiMode.manual,
        overlays: SystemUiOverlay.values,
      );
    }
    discordRpc?.showIdleText();
    final index = pages[_currentIndex!].index;
    if (index != null) {
      _readerController.setPageIndex(_geCurrentIndex(index), true);
    }
    disposePreloadManager();
    _readerController.keepAliveLink?.close();
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.paused ||
        state == AppLifecycleState.detached) {
      final index = pages[_currentIndex!].index;
      if (index != null) {
        _readerController.setPageIndex(_geCurrentIndex(index), true);
      }
    }
  }

  late final _autoScroll = ValueNotifier(
    _readerController.autoScrollValues().$1,
  );
  late final _autoScrollPage = ValueNotifier(_autoScroll.value);
  late GetChapterPagesModel _chapterUrlModel = widget.chapterUrlModel;

  late Chapter chapter = widget.chapter;

  final _failedToLoadImage = ValueNotifier<bool>(false);

  late int? _currentIndex = _readerController.getPageIndex();

  late final ItemScrollController _itemScrollController =
      ItemScrollController();
  final ScrollOffsetController _pageOffsetController = ScrollOffsetController();
  final ItemPositionsListener _itemPositionsListener =
      ItemPositionsListener.create();

  late AnimationController _doubleClickAnimationController;

  Animation<double>? _doubleClickAnimation;
  late DoubleClickAnimationListener _doubleClickAnimationListener;
  List<double> doubleTapScales = <double>[1.0, 2.0];
  final StreamController<double> _rebuildDetail =
      StreamController<double>.broadcast();
  @override
  void initState() {
    super.initState();
    _doubleClickAnimationController = AnimationController(
      duration: _doubleTapAnimationDuration(),
      vsync: this,
    );
    _scaleAnimationController = AnimationController(
      duration: _doubleTapAnimationDuration(),
      vsync: this,
    );
    _animation = Tween(begin: 1.0, end: 2.0).animate(
      CurvedAnimation(curve: Curves.ease, parent: _scaleAnimationController),
    );
    _animation.addListener(() => _photoViewController.scale = _animation.value);
    _itemPositionsListener.itemPositions.addListener(_readProgressListener);
    initPageNavigation(
      itemScrollController: _itemScrollController,
      extendedController: _extendedController,
    );
    _initCurrentIndex();
    discordRpc?.showChapterDetails(ref, chapter);
    WidgetsBinding.instance.addObserver(this);
  }

  // final double _horizontalScaleValue = 1.0;
  bool _isNextChapterPreloading = false;

  late int pagePreloadAmount = ref.read(pagePreloadAmountStateProvider);
  late bool _isBookmarked = _readerController.getChapterBookmarked();

  bool _isLastPageTransition = false;
  final _currentReaderMode = StateProvider<ReaderMode?>(() => null);
  PageMode? _pageMode;
  bool _isView = false;
  Alignment _scalePosition = Alignment.center;
  final PhotoViewController _photoViewController = PhotoViewController();
  final PhotoViewScaleStateController _photoViewScaleStateController =
      PhotoViewScaleStateController();
  final List<int> _cropBorderCheckList = [];

  void _onScaleEnd(
    BuildContext context,
    ScaleEndDetails details,
    PhotoViewControllerValue controllerValue,
  ) {
    if (controllerValue.scale! < 1) {
      _photoViewScaleStateController.reset();
    }
  }

  late final _extendedController = ExtendedPageController(
    initialPage: _currentIndex!,
  );

  double get pixelRatio => View.of(context).devicePixelRatio;

  Size get size => View.of(context).physicalSize / pixelRatio;
  Alignment _computeAlignmentByTapOffset(Offset offset) {
    return Alignment(
      (offset.dx - size.width / 2) / (size.width / 2),
      (offset.dy - size.height / 2) / (size.height / 2),
    );
  }

  Axis _scrollDirection = Axis.vertical;
  bool _isReverseHorizontal = false;

  Color _backgroundColor(BuildContext context) =>
      Theme.of(context).scaffoldBackgroundColor.withValues(alpha: 0.9);

  void _setFullScreen({bool? value}) async {
    if (isDesktop) {
      value = await windowManager.isFullScreen();
      setFullScreen(value: !value);
    }
    ref.read(fullScreenReaderStateProvider.notifier).set(!value!);
  }

  @override
  Widget build(BuildContext context) {
    final backgroundColor = ref.watch(backgroundColorStateProvider);
    final fullScreenReader = ref.watch(fullScreenReaderStateProvider);
    final cropBorders = ref.watch(cropBordersStateProvider);
    final readerMode = ref.watch(_currentReaderMode);
    final bool isHorizontalContinuaous =
        readerMode == ReaderMode.horizontalContinuous;
    if (cropBorders) {
      _processCropBorders();
    }

    final l10n = l10nLocalizations(context)!;
    return ReaderKeyboardHandler(
      onPreviousPage: () => navigationService.previousPage(
        readerMode: readerMode!,
        currentIndex: _currentIndex!,
        animate: true,
      ),
      onNextPage: () => navigationService.nextPage(
        readerMode: readerMode!,
        currentIndex: _currentIndex!,
        maxPages: pages.length,
        animate: true,
      ),
      onEscape: () => _goBack(context),
      onFullScreen: () => _setFullScreen(),
      onNextChapter: () {
        bool hasNextChapter = _readerController.getChapterIndex().$1 != 0;
        if (hasNextChapter) {
          pushReplacementMangaReaderView(
            context: context,
            chapter: _readerController.getNextChapter(),
          );
        }
      },
      onPreviousChapter: () {
        bool hasPrevChapter =
            _readerController.getChapterIndex().$1 + 1 !=
            _readerController.getChaptersLength(
              _readerController.getChapterIndex().$2,
            );
        if (hasPrevChapter) {
          pushReplacementMangaReaderView(
            context: context,
            chapter: _readerController.getPrevChapter(),
          );
        }
      },
    ).wrapWithKeyboardListener(
      isReverseHorizontal: _isReverseHorizontal,
      child: NotificationListener<UserScrollNotification>(
        onNotification: (notification) {
          if (notification.direction == ScrollDirection.idle) {
            if (_isView) {
              _isViewFunction();
            }
          }

          return true;
        },
        child: Material(
          child: SafeArea(
            top: !fullScreenReader,
            bottom: false,
            child: ValueListenableBuilder(
              valueListenable: _failedToLoadImage,
              builder: (context, failedToLoadImage, child) {
                return Stack(
                  children: [
                    _isContinuousMode()
                        ? ImageViewWebtoon(
                            pages: pages,
                            itemScrollController: _itemScrollController,
                            scrollOffsetController: _pageOffsetController,
                            itemPositionsListener: _itemPositionsListener,
                            scrollDirection: isHorizontalContinuaous
                                ? Axis.horizontal
                                : Axis.vertical,
                            minCacheExtent:
                                pagePreloadAmount * context.height(1),
                            initialScrollIndex: _readerController
                                .getPageIndex(),
                            physics: const ClampingScrollPhysics(),
                            onLongPressData: (data) => ImageActionsDialog.show(
                              context: context,
                              data: data,
                              manga: widget.chapter.manga.value!,
                              chapterName: widget.chapter.name!,
                            ),
                            onFailedToLoadImage: (value) {
                              // Handle failed image loading
                              if (_failedToLoadImage.value != value &&
                                  context.mounted) {
                                _failedToLoadImage.value = value;
                              }
                            },
                            backgroundColor: backgroundColor,
                            isDoublePageMode:
                                _pageMode == PageMode.doublePage &&
                                !isHorizontalContinuaous,
                            isHorizontalContinuous: isHorizontalContinuaous,
                            readerMode: ref.watch(_currentReaderMode)!,
                            photoViewController: _photoViewController,
                            photoViewScaleStateController:
                                _photoViewScaleStateController,
                            scalePosition: _scalePosition,
                            onScaleEnd: (details) => _onScaleEnd(
                              context,
                              details,
                              _photoViewController.value,
                            ),
                            onDoubleTapDown: (offset) => _toggleScale(offset),
                            onDoubleTap: () {},
                          )
                        : Material(
                            color: getBackgroundColor(backgroundColor),
                            shadowColor: getBackgroundColor(backgroundColor),
                            child:
                                (_pageMode == PageMode.doublePage &&
                                    !isHorizontalContinuaous)
                                ? ExtendedImageGesturePageView.builder(
                                    controller: _extendedController,
                                    scrollDirection: _scrollDirection,
                                    reverse: _isReverseHorizontal,
                                    physics: const ClampingScrollPhysics(),
                                    canScrollPage: (_) {
                                      return true;
                                    },
                                    itemBuilder: (context, index) {
                                      if (index < pages.length &&
                                          pages[index].isTransitionPage) {
                                        return TransitionViewPaged(
                                          data: pages[index],
                                        );
                                      }

                                      int index1 = index * 2 - 1;
                                      int index2 = index1 + 1;
                                      final pageList = (index == 0
                                          ? [pages[0], null]
                                          : [
                                              index1 < pages.length
                                                  ? pages[index1]
                                                  : null,
                                              index2 < pages.length
                                                  ? pages[index2]
                                                  : null,
                                            ]);
                                      return DoublePageView.paged(
                                        pages: _isReverseHorizontal
                                            ? pageList.reversed.toList()
                                            : pageList,
                                        backgroundColor: backgroundColor,
                                        onFailedToLoadImage: (val) {
                                          if (_failedToLoadImage.value != val &&
                                              mounted) {
                                            _failedToLoadImage.value = val;
                                          }
                                        },
                                        onLongPressData: (datas) {
                                          ImageActionsDialog.show(
                                            context: context,
                                            data: datas,
                                            manga: widget.chapter.manga.value!,
                                            chapterName: widget.chapter.name!,
                                          );
                                        },
                                      );
                                    },
                                    itemCount: (pages.length / 2).ceil() + 1,
                                    onPageChanged: _onPageChanged,
                                  )
                                : ExtendedImageGesturePageView.builder(
                                    controller: _extendedController,
                                    scrollDirection: _scrollDirection,
                                    reverse: _isReverseHorizontal,
                                    physics: const ClampingScrollPhysics(),
                                    canScrollPage: (gestureDetails) {
                                      return true;
                                    },
                                    itemBuilder: (BuildContext context, int index) {
                                      if (pages[index].isTransitionPage) {
                                        return TransitionViewPaged(
                                          data: pages[index],
                                        );
                                      }

                                      return ImageViewPaged(
                                        data: pages[index],
                                        loadStateChanged: (state) {
                                          if (state.extendedImageLoadState ==
                                              LoadState.loading) {
                                            final ImageChunkEvent?
                                            loadingProgress =
                                                state.loadingProgress;
                                            final double progress =
                                                loadingProgress
                                                        ?.expectedTotalBytes !=
                                                    null
                                                ? loadingProgress!
                                                          .cumulativeBytesLoaded /
                                                      loadingProgress
                                                          .expectedTotalBytes!
                                                : 0;
                                            return Container(
                                              color: getBackgroundColor(
                                                backgroundColor,
                                              ),
                                              height: context.height(0.8),
                                              child:
                                                  CircularProgressIndicatorAnimateRotate(
                                                    progress: progress,
                                                  ),
                                            );
                                          }
                                          if (state.extendedImageLoadState ==
                                              LoadState.completed) {
                                            if (_failedToLoadImage.value ==
                                                true) {
                                              Future.delayed(
                                                const Duration(
                                                  milliseconds: 10,
                                                ),
                                              ).then(
                                                (value) =>
                                                    _failedToLoadImage.value =
                                                        false,
                                              );
                                            }
                                            return ExtendedImageGesture(
                                              state,
                                              canScaleImage: (_) => true,
                                              imageBuilder:
                                                  (
                                                    Widget image, {
                                                    ExtendedImageGestureState?
                                                    imageGestureState,
                                                  }) {
                                                    return image;
                                                  },
                                            );
                                          }
                                          if (state.extendedImageLoadState ==
                                              LoadState.failed) {
                                            if (_failedToLoadImage.value ==
                                                false) {
                                              Future.delayed(
                                                const Duration(
                                                  milliseconds: 10,
                                                ),
                                              ).then(
                                                (value) =>
                                                    _failedToLoadImage.value =
                                                        true,
                                              );
                                            }
                                            return Container(
                                              color: getBackgroundColor(
                                                backgroundColor,
                                              ),
                                              height: context.height(0.8),
                                              child: Column(
                                                mainAxisAlignment:
                                                    MainAxisAlignment.center,
                                                children: [
                                                  Text(
                                                    l10n.image_loading_error,
                                                    style: TextStyle(
                                                      color: Colors.white
                                                          .withValues(
                                                            alpha: 0.7,
                                                          ),
                                                    ),
                                                  ),
                                                  Padding(
                                                    padding:
                                                        const EdgeInsets.all(
                                                          8.0,
                                                        ),
                                                    child: GestureDetector(
                                                      onLongPress: () {
                                                        state.reLoadImage();
                                                        _failedToLoadImage
                                                                .value =
                                                            false;
                                                      },
                                                      onTap: () {
                                                        state.reLoadImage();
                                                        _failedToLoadImage
                                                                .value =
                                                            false;
                                                      },
                                                      child: Container(
                                                        decoration: BoxDecoration(
                                                          color: context
                                                              .primaryColor,
                                                          borderRadius:
                                                              BorderRadius.circular(
                                                                30,
                                                              ),
                                                        ),
                                                        child: Padding(
                                                          padding:
                                                              const EdgeInsets.symmetric(
                                                                vertical: 8,
                                                                horizontal: 16,
                                                              ),
                                                          child: Text(
                                                            l10n.retry,
                                                          ),
                                                        ),
                                                      ),
                                                    ),
                                                  ),
                                                ],
                                              ),
                                            );
                                          }
                                          return const SizedBox.shrink();
                                        },
                                        initGestureConfigHandler: (state) {
                                          return GestureConfig(
                                            inertialSpeed: 200,
                                            inPageView: true,
                                            maxScale: 8,
                                            animationMaxScale: 8,
                                            cacheGesture: true,
                                            hitTestBehavior:
                                                HitTestBehavior.translucent,
                                          );
                                        },
                                        onDoubleTap: (state) {
                                          final Offset? pointerDownPosition =
                                              state.pointerDownPosition;
                                          final double? begin =
                                              state.gestureDetails!.totalScale;
                                          double end;

                                          //remove old
                                          _doubleClickAnimation?.removeListener(
                                            _doubleClickAnimationListener,
                                          );

                                          //stop pre
                                          _doubleClickAnimationController
                                              .stop();

                                          //reset to use
                                          _doubleClickAnimationController
                                              .reset();

                                          if (begin == doubleTapScales[0]) {
                                            end = doubleTapScales[1];
                                          } else {
                                            end = doubleTapScales[0];
                                          }

                                          _doubleClickAnimationListener = () {
                                            state.handleDoubleTap(
                                              scale:
                                                  _doubleClickAnimation!.value,
                                              doubleTapPosition:
                                                  pointerDownPosition,
                                            );
                                          };

                                          _doubleClickAnimation =
                                              Tween(
                                                begin: begin,
                                                end: end,
                                              ).animate(
                                                CurvedAnimation(
                                                  curve: Curves.ease,
                                                  parent:
                                                      _doubleClickAnimationController,
                                                ),
                                              );

                                          _doubleClickAnimation!.addListener(
                                            _doubleClickAnimationListener,
                                          );

                                          _doubleClickAnimationController
                                              .forward();
                                        },
                                        onLongPressData: (datas) {
                                          ImageActionsDialog.show(
                                            context: context,
                                            data: datas,
                                            manga: widget.chapter.manga.value!,
                                            chapterName: widget.chapter.name!,
                                          );
                                        },
                                      );
                                    },
                                    itemCount: pages.length,
                                    onPageChanged: _onPageChanged,
                                  ),
                          ),
                    Consumer(
                      builder: (context, ref, child) {
                        final usePageTapZones = ref.watch(
                          usePageTapZonesStateProvider,
                        );
                        return ReaderGestureHandler(
                          usePageTapZones: usePageTapZones,
                          isRTL: _isReverseHorizontal,
                          hasImageError: failedToLoadImage,
                          isContinuousMode: _isContinuousMode(),
                          onToggleUI: _isViewFunction,
                          onPreviousPage: () => navigationService.previousPage(
                            readerMode: readerMode!,
                            currentIndex: _currentIndex!,
                            animate: true,
                          ),
                          onNextPage: () => navigationService.nextPage(
                            readerMode: readerMode!,
                            currentIndex: _currentIndex!,
                            maxPages: pages.length,
                            animate: true,
                          ),
                          onDoubleTapDown: (position) => _toggleScale(position),
                          onDoubleTap: () {},
                          onSecondaryTapDown: (position) =>
                              _toggleScale(position),
                          onSecondaryTap: () {},
                        );
                      },
                    ),
                    ReaderAppBar(
                      chapter: chapter,
                      mangaName: _readerController.getMangaName(),
                      chapterTitle: _readerController.getChapterTitle(),
                      isVisible: _isView,
                      isBookmarked: _isBookmarked,
                      backgroundColor: _backgroundColor,
                      onBackPressed: () => Navigator.pop(context),
                      onBookmarkPressed: () {
                        _readerController.setChapterBookmarked();
                        setState(() {
                          _isBookmarked = !_isBookmarked;
                        });
                      },
                      onWebViewPressed:
                          (chapter.manga.value!.isLocalArchive ?? false) ==
                              false
                          ? () {
                              final data = buildWebViewData(chapter);
                              if (data != null) {
                                context.push("/mangawebview", extra: data);
                              }
                            }
                          : null,
                    ),
                    ReaderBottomBar(
                      chapter: chapter,
                      isVisible: _isView,
                      hasPreviousChapter:
                          _readerController.getChapterIndex().$1 + 1 !=
                          _readerController.getChaptersLength(
                            _readerController.getChapterIndex().$2,
                          ),
                      hasNextChapter:
                          _readerController.getChapterIndex().$1 != 0,
                      onPreviousChapter: () {
                        pushReplacementMangaReaderView(
                          context: context,
                          chapter: _readerController.getPrevChapter(),
                        );
                      },
                      onNextChapter: () {
                        pushReplacementMangaReaderView(
                          context: context,
                          chapter: _readerController.getNextChapter(),
                        );
                      },
                      onSliderChanged: (value, ref) {
                        ref
                            .read(currentIndexProvider(chapter).notifier)
                            .setCurrentIndex(value);
                      },
                      onSliderChangeEnd: (value) {
                        try {
                          final index = pages
                              .firstWhere(
                                (element) =>
                                    element.chapter == chapter &&
                                    element.index == value,
                              )
                              .pageIndex;
                          navigationService.jumpToPage(
                            index: index!,
                            readerMode: ref.read(_currentReaderMode)!,
                          );
                        } catch (_) {}
                      },
                      onReaderModeChanged: (mode, ref) {
                        ref.read(_currentReaderMode.notifier).state = mode;
                        _setReaderMode(mode, ref);
                      },
                      onPageModeToggle: () async {
                        final readerMode = ref.read(_currentReaderMode);
                        if (!(readerMode == ReaderMode.horizontalContinuous)) {
                          navigationService.jumpToPage(
                            index: _pageMode == PageMode.onePage
                                ? (_geCurrentIndex(
                                            pages[_currentIndex!].index!,
                                          ) /
                                          2)
                                      .ceil()
                                : _geCurrentIndex(pages[_currentIndex!].index!),
                            readerMode: ref.read(_currentReaderMode)!,
                          );
                          PageMode newPageMode = _pageMode == PageMode.onePage
                              ? PageMode.doublePage
                              : PageMode.onePage;
                          _readerController.setPageMode(newPageMode);
                          if (mounted) {
                            setState(() {
                              _pageMode = newPageMode;
                            });
                          }
                        }
                      },
                      onSettingsPressed: () => ReaderSettingsModal.show(
                        context: context,
                        vsync: this,
                        currentReaderModeProvider: _currentReaderMode,
                        autoScroll: _autoScroll,
                        autoScrollPage: _autoScrollPage,
                        pageOffset: _pageOffset,
                        onAutoPageScroll: _autoPagescroll,
                        onReaderModeChanged: (mode, widgetRef) {
                          widgetRef.read(_currentReaderMode.notifier).state =
                              mode;
                          _setReaderMode(mode, widgetRef);
                        },
                        onAutoScrollSave: (enabled, offset) {
                          _readerController.setAutoScroll(enabled, offset);
                        },
                        onFullScreenToggle: () {
                          final fullScreen = ref.read(
                            fullScreenReaderStateProvider,
                          );
                          _setFullScreen(value: !fullScreen);
                        },
                      ),
                      currentReaderModeProvider: _currentReaderMode,
                      currentIndexProvider: currentIndexProvider,
                      currentPageMode: _pageMode,
                      isReverseHorizontal: _isReverseHorizontal,
                      totalPages: _readerController.getPageLength(
                        _chapterUrlModel.pageUrls,
                      ),
                      currentIndexLabel: _currentIndexLabel,
                      backgroundColor: _backgroundColor,
                    ),
                    PageIndicator(
                      chapter: chapter,
                      isUiVisible: _isView,
                      totalPages: _readerController.getPageLength(
                        _chapterUrlModel.pageUrls,
                      ),
                      formatCurrentIndex: _currentIndexLabel,
                    ),
                    ReaderAutoScrollButton(
                      isContinuousMode: _isContinuousMode(),
                      isUiVisible: _isView,
                      autoScrollPage: _autoScrollPage,
                      autoScroll: _autoScroll,
                      onToggle: () {
                        _autoPagescroll();
                        _autoScroll.value = !_autoScroll.value;
                      },
                    ),
                  ],
                );
              },
            ),
          ),
        ),
      ),
    );
  }

  Duration? _doubleTapAnimationDuration() {
    int doubleTapAnimationValue = isar.settings
        .getSync(227)!
        .doubleTapAnimationSpeed!;
    if (doubleTapAnimationValue == 0) {
      return const Duration(milliseconds: 10);
    } else if (doubleTapAnimationValue == 1) {
      return const Duration(milliseconds: 800);
    }
    return const Duration(milliseconds: 200);
  }

  void _readProgressListener() async {
    final itemPositions = _itemPositionsListener.itemPositions.value;
    if (itemPositions.isNotEmpty) {
      _currentIndex = itemPositions.first.index;
      int pagesLength =
          (_pageMode == PageMode.doublePage &&
              !(ref.watch(_currentReaderMode) ==
                  ReaderMode.horizontalContinuous))
          ? (pages.length / 2).ceil() + 1
          : pages.length;
      if (_currentIndex! >= 0 && _currentIndex! < pagesLength) {
        try {
          final idx = pages[_currentIndex!].index;
          if (idx != null) {
            _readerController.setPageIndex(_geCurrentIndex(idx), false);
          }
        } catch (_) {}

        if (_readerController.chapter.id != pages[_currentIndex!].chapter!.id) {
          if (mounted) {
            setState(() {
              _readerController = ref.read(
                readerControllerProvider(
                  chapter: pages[_currentIndex!].chapter!,
                ).notifier,
              );

              chapter = pages[_currentIndex!].chapter!;
              final chapterUrlModel = pages[_currentIndex!].chapterUrlModel;

              if (chapterUrlModel != null) {
                _chapterUrlModel = chapterUrlModel;
              }

              _isBookmarked = _readerController.getChapterBookmarked();
            });
          }
        }
        if ((itemPositions.last.index == pagesLength - 1) &&
            !_isLastPageTransition) {
          if (_isNextChapterPreloading) return;
          try {
            _isNextChapterPreloading = true;
            if (!mounted) return;
            final value = await ref.read(
              getChapterPagesProvider(
                chapter: _readerController.getNextChapter(),
              ).future,
            );
            if (mounted) {
              _preloadNextChapter(value, chapter);
              _isNextChapterPreloading = false;
            }
          } on RangeError {
            _isNextChapterPreloading = false;
            _addLastPageTransition(chapter);
          }
        }
        final idx = pages[_currentIndex!].index;
        if (idx != null) {
          ref.read(currentIndexProvider(chapter).notifier).setCurrentIndex(idx);
        }
      }
    }
  }

  void _addLastPageTransition(Chapter chap) {
    if (_isLastPageTransition) return;
    try {
      if (!mounted || pageCount == 0) return;
      if (pages.last.isLastChapter ?? false) return;

      final added = addLastChapterTransition(chap);
      if (added && mounted) {
        setState(() {
          _isLastPageTransition = true;
        });
      }
    } catch (_) {}
  }

  void _preloadNextChapter(GetChapterPagesModel chapterData, Chapter chap) {
    try {
      if (chapterData.uChapDataPreload.isEmpty || !mounted) return;

      final firstChapter = chapterData.uChapDataPreload.first.chapter;
      if (firstChapter == null) return;

      // Use mixin's method for memory-bounded preloading with auto-eviction
      preloadNextChapter(chapterData, chap).then((success) {
        if (success && mounted) {
          setState(() {});
        }
      });
    } catch (_) {}
  }

  void _initCurrentIndex() async {
    final readerMode = _readerController.getReaderMode();

    // Initialize the preload manager with bounded memory (from ReaderMemoryManagement mixin)
    initializePreloadManager(
      _chapterUrlModel,
      startIndex: _currentIndex ?? 0,
      onPagesUpdated: () {
        if (mounted) setState(() {});
      },
      onIndexAdjusted: (newIndex) {
        if (mounted) {
          setState(() {
            _currentIndex = newIndex;
          });
        }
      },
    );

    _readerController.setMangaHistoryUpdate();
    await Future.delayed(const Duration(milliseconds: 1));
    final fullScreenReader = ref.watch(fullScreenReaderStateProvider);
    if (fullScreenReader) {
      if (isDesktop) {
        setFullScreen(value: true);
      } else {
        SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
      }
    }
    ref.read(_currentReaderMode.notifier).state = readerMode;
    if (mounted) {
      setState(() {
        _pageMode = _readerController.getPageMode();
      });
    }
    _setReaderMode(readerMode, ref);
    // if (pageCount > 0 && _currentIndex != null && _currentIndex! < pageCount) {
    //   ref
    //       .read(currentIndexProvider(chapter).notifier)
    //       .setCurrentIndex(pages[_currentIndex!].index!);
    // }

    if (readerMode != ReaderMode.verticalContinuous &&
        readerMode != ReaderMode.webtoon) {
      _autoScroll.value = false;
    }
    _autoPagescroll();
    if (_readerController.getPageLength(_chapterUrlModel.pageUrls) == 1 &&
        (readerMode == ReaderMode.ltr ||
            readerMode == ReaderMode.rtl ||
            readerMode == ReaderMode.vertical)) {
      _onPageChanged(0);
    }
  }

  Future<void> _onPageChanged(int index) async {
    final cropBorders = ref.watch(cropBordersStateProvider);
    if (cropBorders) {
      _processCropBordersByIndex(index);
    }
    final idx = pages[_currentIndex!].index;
    if (idx != null) {
      _readerController.setPageIndex(_geCurrentIndex(idx), false);
    }
    if (_readerController.chapter.id != pages[index].chapter!.id) {
      if (mounted) {
        setState(() {
          _readerController = ref.read(
            readerControllerProvider(
              chapter: pages[_currentIndex!].chapter!,
            ).notifier,
          );
          chapter = pages[_currentIndex!].chapter!;
          final chapterUrlModel = pages[index].chapterUrlModel;
          if (chapterUrlModel != null) {
            _chapterUrlModel = chapterUrlModel;
          }
          _isBookmarked = _readerController.getChapterBookmarked();
        });
      }
    }
    _currentIndex = index;
    if (pages[index].index != null) {
      ref
          .read(currentIndexProvider(chapter).notifier)
          .setCurrentIndex(pages[index].index!);
    }

    if ((pages[index].pageIndex! == pages.length - 1) &&
        !_isLastPageTransition) {
      if (_isNextChapterPreloading) return;
      try {
        _isNextChapterPreloading = true;
        if (!mounted) return;
        final value = await ref.watch(
          getChapterPagesProvider(
            chapter: _readerController.getNextChapter(),
          ).future,
        );
        if (mounted) {
          _preloadNextChapter(value, chapter);
          _isNextChapterPreloading = false;
        }
      } on RangeError {
        _isNextChapterPreloading = false;
        _addLastPageTransition(chapter);
      }
    }
  }

  late final _pageOffset = ValueNotifier(
    _readerController.autoScrollValues().$2,
  );

  void _autoPagescroll() async {
    if (_isContinuousMode()) {
      for (int i = 0; i < 1; i++) {
        await Future.delayed(const Duration(milliseconds: 100));
        if (!_autoScroll.value) {
          return;
        }
        _pageOffsetController.animateScroll(
          offset: _pageOffset.value,
          duration: const Duration(milliseconds: 100),
        );
      }
      _autoPagescroll();
    }
  }

  void _toggleScale(Offset tapPosition) {
    if (mounted) {
      setState(() {
        if (_scaleAnimationController.isAnimating) {
          return;
        }

        if (_photoViewController.scale == 1.0) {
          _scalePosition = _computeAlignmentByTapOffset(tapPosition);

          if (_scaleAnimationController.isCompleted) {
            _scaleAnimationController.reset();
          }

          _scaleAnimationController.forward();
          return;
        }

        if (_photoViewController.scale == 2.0) {
          _scaleAnimationController.reverse();
          return;
        }

        _photoViewScaleStateController.reset();
      });
    }
  }

  void _setReaderMode(ReaderMode value, WidgetRef ref) async {
    if (value != ReaderMode.verticalContinuous && value != ReaderMode.webtoon) {
      _autoScroll.value = false;
    } else {
      if (_autoScrollPage.value) {
        _autoPagescroll();
        _autoScroll.value = true;
      }
    }

    _failedToLoadImage.value = false;
    _readerController.setReaderMode(value);

    int index =
        (_pageMode == PageMode.doublePage &&
            !(ref.watch(_currentReaderMode) == ReaderMode.horizontalContinuous))
        ? (_currentIndex! / 2).ceil()
        : _currentIndex!;
    ref.read(_currentReaderMode.notifier).state = value;
    if (value == ReaderMode.vertical) {
      if (mounted) {
        setState(() {
          _scrollDirection = Axis.vertical;
          _isReverseHorizontal = false;
        });
        await Future.delayed(const Duration(milliseconds: 30));

        _extendedController.jumpToPage(index);
      }
    } else if (value == ReaderMode.ltr || value == ReaderMode.rtl) {
      if (mounted) {
        setState(() {
          if (value == ReaderMode.rtl) {
            _isReverseHorizontal = true;
          } else {
            _isReverseHorizontal = false;
          }

          _scrollDirection = Axis.horizontal;
        });
        await Future.delayed(const Duration(milliseconds: 30));

        _extendedController.jumpToPage(index);
      }
    } else {
      if (mounted) {
        setState(() {
          _isReverseHorizontal = false;
        });
        await Future.delayed(const Duration(milliseconds: 30));
        _itemScrollController.scrollTo(
          index: index,
          duration: const Duration(milliseconds: 1),
          curve: Curves.ease,
        );
      }
    }
  }

  void _processCropBordersByIndex(int index) async {
    if (!_cropBorderCheckList.contains(index)) {
      _cropBorderCheckList.add(index);
      if (!mounted) return;
      final value = await ref.read(
        cropBordersProvider(data: pages[index], cropBorder: true).future,
      );
      if (mounted) {
        setState(() {
          pages[index] = pages[index]..cropImage = value;
        });
      }
    }
  }

  void _processCropBorders() async {
    if (_cropBorderCheckList.length == pages.length) return;

    for (var i = 0; i < pages.length; i++) {
      if (!_cropBorderCheckList.contains(i)) {
        _cropBorderCheckList.add(i);
        if (!mounted) return;
        final value = await ref.read(
          cropBordersProvider(data: pages[i], cropBorder: true).future,
        );
        if (mounted) {
          setState(() {
            pages[i] = pages[i]..cropImage = value;
          });
        }
      }
    }
  }

  void _goBack(BuildContext context) {
    SystemChrome.setEnabledSystemUIMode(
      SystemUiMode.manual,
      overlays: SystemUiOverlay.values,
    );
    Navigator.pop(context);
  }

  void _isViewFunction() {
    final fullScreenReader = ref.watch(fullScreenReaderStateProvider);
    if (context.mounted) {
      setState(() {
        _isView = !_isView;
      });
    }
    if (fullScreenReader) {
      if (_isView) {
        SystemChrome.setEnabledSystemUIMode(
          SystemUiMode.manual,
          overlays: SystemUiOverlay.values,
        );
      } else {
        SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
      }
    }
  }

  String _currentIndexLabel(int index) {
    if (_pageMode != PageMode.doublePage) {
      return "${index + 1}";
    }
    if (index == 0) {
      return "1";
    }
    int pageLength = _readerController.getPageLength(_chapterUrlModel.pageUrls);
    int index1 = index * 2;
    int index2 = index1 + 1;
    return !(index * 2 < pageLength) ? "$pageLength" : "$index1-$index2";
  }

  int _geCurrentIndex(int index) {
    if (_pageMode != PageMode.doublePage || index == 0) {
      return index;
    }
    int pageLength = _readerController.getPageLength(_chapterUrlModel.pageUrls);
    int index1 = index * 2;
    return !(index * 2 < pageLength) ? pageLength - 1 : index1 - 1;
  }

  bool _isContinuousMode() {
    final readerMode = ref.watch(_currentReaderMode);
    return readerMode == ReaderMode.verticalContinuous ||
        readerMode == ReaderMode.webtoon ||
        readerMode == ReaderMode.horizontalContinuous;
  }
}
